package gen

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"

	"gitee.com/krio/ginc/parse"
	"gitee.com/krio/ginc/util"
	"github.com/gookit/color"
)

// Generator is responsible to init project and generates codes from proto.
type Generator struct {
	FromProtoPath      string   // proto文件根目录
	FromThirdPartyPath []string // 第三方文件引用根目录
	FromPbPath         string   // pb文件生成根目录
	ToHandlerPath      string   // 处理函数文件
	ToRoute            string   // 路由文件生成目录
	ToDocPath          string   // 接口文档文件生成地址
	tempPbPath         string
	protoFiles         []string
	goModule           string
	protoList          []*parse.ParseProtoInfo
}

func (g *Generator) Run() error {
	var err error
	// 获取所有的proto文件
	g.protoFiles, err = util.GetProtoFiles(g.FromProtoPath)
	if err != nil {
		return fmt.Errorf("ginc gen failed: %+v", err)
	}
	if len(g.protoFiles) <= 0 {
		return fmt.Errorf("未发现proto相关文件: %s", g.FromProtoPath)
	}
	// 获取项目的module
	g.goModule, err = util.GetModuleName()
	if err != nil {
		return err
	}
	g.tempPbPath = filepath.Join(g.goModule, g.FromPbPath)
	// 打印参数
	g.cmdPrint()
	// 生成pb、swagger等文件
	if err = g.cmd(); err != nil {
		return err
	}
	// 获取proto文件信息
	result := &parse.Result{}
	g.protoList, result, err = parse.ParseProto(g.protoFiles)
	if err != nil {
		return fmt.Errorf("ParseProto Error : %+v", err)
	}
	if len(result.ErrList) > 0 {
		util.PrintFails(result.ErrList)
		util.PrintEndFail("操作失败")
	}
	// 处理tag
	if err = g.HandleTagGen(); err != nil {
		return fmt.Errorf("HandleTagGen Error : %+v", err)
	}
	if g.ToHandlerPath != "" {
		// 生成handler模板文件
		result.NewAddCount, err = g.GenHandler()
		if err != nil {
			return fmt.Errorf("GenHandler Error : %+v", err)
		}
	}
	if g.ToRoute != "" {
		if err = g.GenRoute(); err != nil {
			return fmt.Errorf("GenRoute Error : %+v", err)
		}
	}
	// 处理pb文件夹目录
	if err = g.HandlePbDir(); err != nil {
		return fmt.Errorf("HandlePbDir Error : %+v", err)
	}
	// 处理swagger
	if g.ToDocPath != "" {
		if err = g.FormatDocFile(); err != nil {
			return fmt.Errorf("FormatDocFile Error : %+v", err)
		}
	}
	color.Cyanp(fmt.Sprintf("|* service数量:%d \n", result.ServiceCount))
	color.Cyanp(fmt.Sprintf("|* api数量:%d \n", result.ApiCount))
	color.Cyanp(fmt.Sprintf("|* api新增数量:%d \n", result.NewAddCount))
	return nil
}

// 执行cmd命令：生成pb、swagger等
func (g *Generator) cmd() error {
	color.Bluep("[ 执行命令：protoc ]", "\n")
	if err := util.FileExistsOrCreate(g.FromPbPath); err != nil {
		return err
	}
	var args []string
	args = append(args, fmt.Sprintf("--proto_path=%s", g.FromProtoPath)) // proto
	for _, thirdPartyPath := range g.FromThirdPartyPath {
		args = append(args, fmt.Sprintf("--proto_path=%s", thirdPartyPath)) // third_party
	}
	args = append(args, "--go_out=.")
	if g.ToDocPath != "" {
		if err := util.FileExistsOrCreate(g.ToDocPath); err != nil {
			return err
		}
		args = append(args, fmt.Sprintf("--openapiv2_out=allow_merge=true:%s", g.ToDocPath)) // swagger
	}
	// 获取所有proto文件名称
	for _, v := range args {
		color.Cyanp("|* ", v, "\n")
	}
	args = append(args, g.protoFiles...)
	cmd := exec.Command("protoc", args...)
	cmd.Stderr = os.Stderr
	_, err := cmd.Output()
	if err != nil {
		return err
	}

	return nil
}

func (g *Generator) cmdPrint() {
	// 打印配置参数
	color.Bluep("[ 配置列表 ]", "\n")
	color.Cyanp(fmt.Sprintf("|* proto:       %s \n", g.FromProtoPath))
	color.Cyanp(fmt.Sprintf("|* pb:          %s \n", g.FromPbPath))
	color.Cyanp(fmt.Sprintf("|* swagger:     %s \n", g.ToDocPath))
	color.Cyanp(fmt.Sprintf("|* third_party: %s \n", g.FromThirdPartyPath))
	color.Cyanp(fmt.Sprintf("|* handler:     %s \n", g.ToHandlerPath))
	color.Cyanp(fmt.Sprintf("|* router:      %s \n", g.ToRoute))
	color.Cyanp(fmt.Sprintf("|* proto文件数量:%d \n", len(g.protoFiles)))

}
